﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;



namespace Lon.Util
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Variable
    {
        public static readonly Variable Invalid;
        private object objVal;
        private object defValue;

        private VarType varType;

        public VarType VarType
        {
            get { return varType; }
            set { varType = value; }
        }
        public Variable(int val)
        {
            this = new Variable();
            this.objVal = val;
            this.DataType =  VarType.Int;
        }

        public Variable(long val)
        {
            this = new Variable();
            this.objVal = val;
            this.DataType =  VarType.Long;
        }

        public Variable(float val)
        {
            this = new Variable();
            this.objVal = val;
            this.DataType =  VarType.Float;
        }

        public Variable(bool val)
        {
            this = new Variable();
            this.objVal = val;
            this.DataType =  VarType.Bool;
        }

        public Variable(string val)
        {
            this = new Variable();
            this.objVal = val;
            this.DataType =  VarType.String;
        }

        public Variable(byte[] buf)
        {
            this = new Variable();
            this.objVal = buf;
            this.DataType =  VarType.Binary;
        }

        public Variable(DateTime val)
        {
            this = new Variable();
            this.objVal = val;
            this.DataType =  VarType.DateTime;
        }

        public Variable(object obj)
        {
            this = new Variable();
            this.objVal = obj;
            if (obj != null)
            {
                if (obj is string)
                {
                    this.DataType =  VarType.String;
                }
                else if (obj is int)
                {
                    this.DataType =  VarType.Int;
                }
                else if (obj is float)
                {
                    this.DataType =  VarType.Float;
                }
                else if (obj is bool)
                {
                    this.DataType =  VarType.Bool;
                }
                else if (obj is byte[])
                {
                    this.DataType =  VarType.Binary;
                }
                else if (obj is long)
                {
                    this.DataType =  VarType.Long;
                }
                else if (obj is DateTime)
                {
                    this.DataType =  VarType.DateTime;
                }
                else if (obj is Variable)
                {
                    Variable v = (Variable)obj;
                    this.objVal = v.Value;
                    this.DataType = v.DataType;
                }
                else
                {
                    this.DataType =  VarType.Object;
                }
            }
            else
            {
                this.DataType =  VarType.Object;
            }
        }

        public void SetDefValue(object obj)
        {
            this.defValue = obj;
        }

        public static implicit operator Variable(int val)
        {
            return new Variable(val);
        }

        public static implicit operator Variable(long val)
        {
            return new Variable(val);
        }

        public static implicit operator Variable(float val)
        {
            return new Variable(val);
        }

        public static implicit operator Variable(bool val)
        {
            return new Variable(val);
        }

        public static implicit operator Variable(string val)
        {
            return new Variable(val);
        }

        public static implicit operator Variable(byte[] byt)
        {
            return new Variable(byt);
        }

        public static implicit operator Variable(DateTime t)
        {
            return new Variable(t);
        }

        public static implicit operator int(Variable val)
        {
            return val.ToInt();
        }

        public static implicit operator uint(Variable val)
        {
            return (uint)val.ToInt();
        }

        public static implicit operator long(Variable val)
        {
            return val.ToLong();
        }

        public static implicit operator float(Variable val)
        {
            return val.ToFloat();
        }

        public static implicit operator bool(Variable val)
        {
            return val.ToBool();
        }

        public static implicit operator string(Variable val)
        {
            return val.ToVaString();
        }

        public static implicit operator DateTime(Variable val)
        {
            return val.ToDateTime();
        }

        public static implicit operator byte[](Variable val)
        {
            return val.ToBytes();
        }

        private int ToInt()
        {
            int def = 0;
            if ((this.defValue != null) && (this.defValue is int))
            {
                def = (int)this.defValue;
            }
            if (this.Value == null)
            {
                return def;
            }
            switch (this.DataType)
            {
                case  VarType.Bool:
                    return (this.bVal ? 1 : def);

                case  VarType.String:
                    StringHelper.TryToInt(this.strVal, ref def);
                    return def;
            }
            return Convert.ToInt32(this.Value);
        }

        private long ToLong()
        {
            long def = 0;
            if ((this.defValue != null) && (this.defValue is long))
            {
                def = (long)this.defValue;
            }
            if (this.Value == null)
            {
                return def;
            }
            switch (this.DataType)
            {
                case  VarType.Bool:
                    return (this.bVal ? 1 : def);

                case  VarType.String:
                    StringHelper.TryToLong(this.strVal, ref def);
                      return def;
            }
            return Convert.ToInt64(this.Value);
        }

        private float ToFloat()
        {
            float def = 0f;
            if ((this.defValue != null) && (this.defValue is float))
            {
                def = (float)this.defValue;
            }
            if (this.Value == null)
            {
                return def;
            }
            switch (this.DataType)
            {
                case  VarType.Bool:
                    return (this.bVal ? 1f : def);

                case  VarType.String:
                    StringHelper.TryToFloat(this.strVal, ref def);
                     return def;
            }
            return Convert.ToSingle(this.Value);
        }

        private bool ToBool()
        {
            bool def = false;
            if ((this.defValue != null) && (this.defValue is bool))
            {
                def = (bool)this.defValue;
            }
            if (this.Value == null)
            {
                return def;
            }
            switch (this.DataType)
            {
                case  VarType.Int:
                    return (this.iVal != 0);

                case  VarType.Float:
                    return ((this.fVal != 0f) && float.IsNaN(this.fVal));

                case  VarType.Bool:
                    return this.bVal;

                case  VarType.String:
                    if (((this.strVal != "1") && (this.strVal != "是")) && !(this.strVal.ToUpper() == "TRUE"))
                    {
                        return false;
                    }
                    return true;

                case  VarType.Object:
                    return this.bVal;
            }
            return false;
        }

        private string ToVaString()
        {
            string def = "";
            if ((this.defValue != null) && (this.defValue is string))
            {
                def = (string)this.defValue;
            }
            if (this.Value == null)
            {
                return def;
            }
            return this.Value.ToString();
        }

        private DateTime ToDateTime()
        {
            DateTime def = DateTime.MinValue;
            if ((this.defValue != null) && (this.defValue is DateTime))
            {
                def = (DateTime)this.defValue;
            }
            if ((this.Value != null) && (this.Value is DateTime))
            {
                return this.dtVal;
            }
            return def;
        }

        private byte[] ToBytes()
        {
            byte[] def = null;
            if ((this.defValue != null) && (this.defValue is byte[]))
            {
                def = (byte[])this.defValue;
            }
            if (this.Value == null)
            {
                return def;
            }
            switch (this.DataType)
            {
                case  VarType.Int:
                    return BitConverter.GetBytes(this.iVal);

                case  VarType.Float:
                    return BitConverter.GetBytes(this.fVal);

                case  VarType.Bool:
                    return BitConverter.GetBytes(this.bVal);

                case  VarType.String:
                    return Encoding.Unicode.GetBytes(this.strVal);

                case  VarType.Binary:
                    return this.binBuf;

                case  VarType.DateTime:
                    return BitConverter.GetBytes(this.dtVal.Ticks);
            }
            return null;
        }

        public static bool operator ==(Variable v1, Variable v2)
        {
            if (v1.DataType == v2.DataType)
            {
                return object.Equals(v1.objVal, v2.objVal);
            }
            return v1.Equals(v2);
        }

        public static bool operator !=(Variable v1, Variable v2)
        {
            if (v1.DataType == v2.DataType)
            {
                return !object.Equals(v1.objVal, v2.objVal);
            }
            return !v1.Equals(v2);
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (obj.GetType() == typeof(Variable))
            {
                return (this.objVal == ((Variable)obj).objVal);
            }
            return (obj == this.objVal);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static Variable operator +(Variable a, Variable b)
        {
            switch (a.DataType)
            {
                case  VarType.Int:
                    if (b.DataType !=  VarType.Float)
                    {
                        if (b.DataType ==  VarType.Long)
                        {
                            return (a.iVal + b.lVal);
                        }
                        return (a.iVal + b);
                    }
                    return (a.iVal + b.fVal);

                case  VarType.Float:
                    return (a.fVal + ((float)b));

                case  VarType.Bool:
                    return (a.bVal | b);

                case  VarType.String:
                    return (a.strVal + ((string)b));

                case  VarType.Binary:
                case  VarType.Object:
                case  VarType.Long:
                    return a;

                case  VarType.DateTime:
                    if (b.DataType !=  VarType.Int)
                    {
                        return a;
                    }
                    if (!(a.dtVal != DateTime.MinValue) || !(a.dtVal != DateTime.MaxValue))
                    {
                        return a;
                    }
                    return a.dtVal.AddSeconds((double)b.iVal);
            }
            return a;
        }

        public static Variable operator -(Variable a, Variable b)
        {
            switch (a.DataType)
            {
                case  VarType.Int:
                    if (b.DataType !=  VarType.Float)
                    {
                        return (a.iVal - b);
                    }
                    return (a.iVal - b.fVal);

                case  VarType.Float:
                    return (a.fVal - ((float)b));

                case  VarType.DateTime:
                    if ((b.DataType ==  VarType.Int) && ((a.dtVal != DateTime.MinValue) && (a.dtVal != DateTime.MaxValue)))
                    {
                        return a.dtVal.AddSeconds((double)-b.iVal);
                    }
                    return a;
            }
            return a;
        }

        public static Variable operator *(Variable a, Variable b)
        {
            switch (a.DataType)
            {
                case  VarType.Int:
                    if (b.DataType !=  VarType.Float)
                    {
                        return (a.iVal * (int)b);
                    }
                    return (a.iVal * b.fVal);

                case  VarType.Float:
                    return (a.fVal * ((float)b));

                case  VarType.Bool:
                    return (!a.bVal ? (Variable)0 : b);
            }
            return a;
        }

        public static Variable operator /(Variable a, Variable b)
        {
            switch (a.DataType)
            {
                case  VarType.Int:
                    return (((float)a.iVal) / ((float)b));

                case  VarType.Float:
                    return (a.fVal / ((float)b));
            }
            return a;
        }

        public override string ToString()
        {
            if (this.Value == null)
            {
                return "";
            }
            switch (this.DataType)
            {
                case  VarType.Int:
                    return ("Int:" + this.iVal.ToString());

                case  VarType.Float:
                    return ("Float:" + this.fVal.ToString());

                case  VarType.Bool:
                    return ("Bool:" + (this.bVal ? "True" : "False"));

                case  VarType.String:
                    return this.strVal;

                case  VarType.Binary:
                    return ("Binary:" + StringHelper.ByteArr2HexString(this.binBuf));

                case  VarType.Long:
                    return ("Long:" + this.objVal.ToString());

                case  VarType.DateTime:
                    return ("DateTime:" + ((DateTime)this.Value).ToString("yyyy-MM-dd HH:mm:ss"));
            }
            return this.objVal.ToString();
        }

        //ToDo  辉煌原代码通用行不强 待修改
 
        public static Variable Parse(string str)
        {
            if (!string.IsNullOrEmpty(str))
            {
                int index = str.IndexOf(':');
                if (index > 0)
                {
                    string type = str.Substring(0, index);
                    string strVal = str.Substring(index + 1);
                    switch (type)
                    {
                        case "Int":
                            return StringHelper.ToInt(strVal, 0);

                        case "Long":
                            return StringHelper.ToLong(strVal, 0);

                        case "Float":
                            return StringHelper.ToFloat(strVal, 0f);

                        case "Bool":
                            return (strVal.ToUpper() == "TRUE");

                        case "String":
                            return new Variable(strVal);

                        case "Binary":
                            return new Variable(StringHelper.BinaryFromString(strVal));

                        case "DateTime":
                            return new Variable(StringHelper.ToDateTime(strVal));
                    }
                }
                return new Variable(str);
            }
            return Invalid;
        }

        public int GetValByteLen()
        {
            switch (this.DataType)
            {
                case  VarType.Int:
                case  VarType.Float:
                    return 4;

                case  VarType.Bool:
                    return 1;

                case  VarType.String:
                    return ((this.strVal == null) ? 4 : (4 + (this.strVal.Length * 2)));

                case  VarType.Binary:
                    return (4 + ((this.binBuf == null) ? 0 : this.binBuf.Length));

                case  VarType.Long:
                    return 8;

                case  VarType.DateTime:
                    return 8;
            }
            return 0;
        }

        public int WriteValBytes(BinaryWriter bw)
        {
            switch (this.DataType)
            {
                case  VarType.Int:
                    bw.Write(this.iVal);
                    return 4;

                case  VarType.Float:
                    bw.Write(this.fVal);
                    return 4;

                case  VarType.Bool:
                    bw.Write(this.bVal ? ((byte)1) : ((byte)0));
                    return 1;

                case  VarType.String:
                    if (!string.IsNullOrEmpty(this.strVal))
                    {
                        bw.Write(this.strVal.Length);
                        bw.Write(this.strVal.ToCharArray());
                        return (4 + (this.strVal.Length * 2));
                    }
                    bw.Write(0);
                    return 0;

                case  VarType.Binary:
                    if (this.binBuf != null)
                    {
                        bw.Write(this.binBuf.Length);
                        bw.Write(this.binBuf);
                        return (4 + this.binBuf.Length);
                    }
                    bw.Write(0);
                    return 4;

                case  VarType.Long:
                    bw.Write((long)this.objVal);
                    return 8;

                case  VarType.DateTime:
                    bw.Write(this.dtVal.Ticks);
                    return 8;
            }
            return 0;
        }

        public static Variable ReadValBytes(BinaryReader br,  VarType keyType)
        {
            int len;
            switch (keyType)
            {
                case  VarType.Int:
                    return br.ReadInt32();

                case  VarType.Float:
                    return br.ReadSingle();

                case  VarType.Bool:
                    return (br.ReadByte() == 1);

                case  VarType.String:
                    len = br.ReadInt32();
                    return new string(br.ReadChars(len));

                case  VarType.Binary:
                    len = br.ReadInt32();
                    if (len <= 0)
                    {
                        return new byte[0];
                    }
                    return br.ReadBytes(len);

                case  VarType.Long:
                    return br.ReadInt64();

                case  VarType.DateTime:
                    return new DateTime(br.ReadInt64());
            }
            return 0;
        }

        public  VarType DataType
        {

            get
            {
                return this.varType;
            }

            private set
            {
                this.varType = value;
            }
        }
        public object Value
        {
            get
            {
                return this.objVal;
            }
        }
        private int iVal
        {
            get
            {
                return (int)this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        private long lVal
        {
            get
            {
                return (long)this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        private float fVal
        {
            get
            {
                return (float)this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        private string strVal
        {
            get
            {
                return (string)this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        private byte[] binBuf
        {
            get
            {
                return (byte[])this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        private bool bVal
        {
            get
            {
                return (bool)this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        private DateTime dtVal
        {
            get
            {
                return (DateTime)this.objVal;
            }
            set
            {
                this.objVal = value;
            }
        }
        static Variable()
        {
            Invalid = new Variable();
        }
    }

}
